import os
import random
import time
import traceback

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from .new_bqg import NewBQG
from .bqg import BQG
from .ThreeRead import ThreeQRead
from requests import Session


# 抓取书籍基础类
# 无头浏览器驱动下载地址：https://googlechromelabs.github.io/chrome-for-testing/#stable
class BaseBook:
    # 查询小说网址
    find_novel_url = "https://bqg123.net/search?form=biqusoso&keyword={}"
    # 项目目录
    root_path = os.getcwd()
    # 浏览器驱动
    executable_path = "\chromedriver\\127\\chromedriver.exe"
    # 浏览器句柄
    browser = None
    # 驱动类地址
    driver = None
    # 驱动类地址
    driver_name = None
    # 请求句柄
    session = None

    # 初始化参数
    def __init__(self, driver_name=False, is_browser=True, root_path=None):
        """
            初始化参数
        :param driver_name:         驱动类
        :param is_browser:          是否使用浏览器
        :param root_path:           项目路径
        """
        driver_name = driver_name if driver_name else "NewBQG"
        self.driver_name = driver_name
        self.driver = eval(f"{driver_name}()")

        # 驱动地址
        root_path = root_path if root_path else self.root_path
        service = ChromeService(executable_path=root_path + self.executable_path)

        # 访问参数设置
        chrome_options = Options()
        chrome_options.add_argument("--headless")# 不打开窗口
        chrome_options.add_argument("--disable-infobars")
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option('useAutomationExtension', False)

        if self.driver:
            # 创建句柄
            self.browser = webdriver.Chrome(service=service, options=chrome_options)

        if not is_browser:
            self.session = Session()

    def css_show(self, url, css_value):
        """
            搜索元素出现
        :param url:         请求地址
        :param css_value:   css的值
        :return:
        """
        self.browser.get(url)

        # 获取文件内容
        soup = BeautifulSoup(self.browser.page_source, 'lxml')
        if css_value == '-1':
            return soup
        else:
            return soup.select(css_value)

    # 等待元素出现
    def wait_css_show(self, url, css_type, css_value, get_css=None, refresh=False, wait_time=1, timeout=10, run_number=5):
        """
            等待元素出现
        :param wait_time:   等待加载时间
        :param url:         请求地址
        :param css_type:    等待css类型
        :param css_value:   等待css的值
        :param get_css:     获取css的值
        :param refresh:     是否刷新页面
        :param timeout:     等待超时
        :param run_number:  重复运行次数
        :return:
        """
        if run_number <= 0:
            return None
        try:
            self.browser.get(url)

            # 额外等待时间
            time.sleep(wait_time)

            # 刷新一下页面
            if refresh:
                self.browser.refresh()

            # 等待元素出现
            wait = WebDriverWait(self.browser, timeout)  # 10秒的超时时间
            wait.until(EC.presence_of_element_located((eval(f"By.{css_type.upper()}"), css_value)))

            # 获取文件内容
            get_css = get_css if get_css else css_value
            soup = BeautifulSoup(self.browser.page_source, 'lxml')
            if get_css == '-1':
                return soup
            else:
                return soup.select(get_css)

        except TimeoutException:
            print("连接超时", url, run_number)
            time.sleep(random.randint(2, 5))
            return self.wait_css_show(url, css_type, css_value, get_css, refresh, wait_time, timeout, run_number-1)

    # 发送消息并点击
    def send_message_chick(self, url, css_type, css_value, message):
        self.browser.get(url)
        # 等待元素出现
        wait = WebDriverWait(self.browser, 10)  # 10秒的超时时间
        tag = wait.until(EC.presence_of_element_located((eval(f"By.{css_type.upper()}"), css_value)))
        tag.send_keys(message)
        tag.send_keys(Keys.ENTER)
        time.sleep(2)

    # 解析css 获取内容
    def parse_select(self, css):
        """
            等待元素出现
        :param css:    css地址
        :return:
        """
        soup = BeautifulSoup(self.browser.page_source, 'lxml')
        return soup.select(css)

    # 获取http数据
    def parse_http_data(self, css, method, url, params=None, json=None, headers=None):
        """
            获取http数据
        :param css:         css
        :param method:      方式
        :param url:         地址
        :param params:      参数
        :param json:        json数据
        :param headers:     请求头
        :return:
        """
        if params is None:
            params = {}
        if headers is None:
            headers = {}
        if json is None:
            json = {}
        if method == 'post':
            res = self.session.post(url, data=params, json=json, headers=headers)
        else:
            res = self.session.get(url, params=params, json=json, headers=headers)
        soup = BeautifulSoup(res.text, 'lxml')
        if css:
            return soup.select(css)
        else:
            return soup

    # 搜索书籍
    def search_books(self, novel_name):
        """
            搜索书籍
        :param novel_name:
        :return:
        """
        try:
            return self.driver.search_books(self, novel_name)
        except Exception:
            print("搜索书籍 异常", traceback.format_exc())
            return []
        finally:
            if self.browser:
                self.browser.close()

    # 获取章节内容
    def get_menu_content(self, novel_url, start_chapter=1, end_chapter=0):
        """
            获取章节级内容
        :param end_chapter:     开始章节
        :param start_chapter:   结束章节
        :param novel_url:       小说网址
        :return:
        """
        try:
            # 获取章节信息
            end_chapter = (start_chapter + 1000) if end_chapter == 0 else end_chapter
            chapters = self.driver.get_characters(self, novel_url, int(start_chapter), int(end_chapter))
            for chapter in chapters:
                # 获取章节内容
                content = self.driver.get_content(self, chapter.get("address"))
                chapter.update(content=content, driver_name=self.driver_name)
                yield chapter
        except Exception:
            print("获取章节信息失败", traceback.format_exc())

        finally:
            if self.browser:
                self.browser.close()

    # 获取最新章节
    def get_last_new_chapter(self, novel_url):
        """
            获取最新章节
        :param novel_url:       小说网址
        :return:
        """
        # 获取章节信息
        try:
            return self.driver.get_last_new_chapter(self, novel_url)
        except Exception:
            print("获取最新章节 异常: {}".format(novel_url), traceback.format_exc())
            return 0
        finally:
            if self.browser:
                self.browser.close()

    def get_test(self):
        # By.CLASS_NAME
        # return self.driver.search_books(self, "守村人，棺中妻")    # 搜索书籍
        # return self.driver.get_characters(self, "https://sk.3qdu.com/xiaoshuo/534373/", 50, 100)  # 获取章节
        # return self.driver.get_content(self, "https://sk.3qdu.com/xiaoshuo/534373/92914553.html") # 获取内容

        return self.driver.get_characters(self, "https://sk.3qdu.com/xiaoshuo/516991/", 20, 150) # 获取最新章节
        pass